MCP server integration + per-conversation vault binding#2
Merged
RaghavSood merged 7 commits intomainfrom Feb 24, 2026
Merged
Conversation
5 tasks
neavra
reviewed
Feb 23, 2026
Contributor
neavra
left a comment
There was a problem hiding this comment.
Left some comments! Please take a look
cmd/server/main.go
Outdated
| }) | ||
|
|
||
| // Agent routes (authenticated) | ||
| agent := e.Group("/agent", server.AuthMiddleware) |
Contributor
There was a problem hiding this comment.
Agent routes should be authenticated so that only vault owners can access conversations
internal/service/agent/tools.go
Outdated
| } | ||
|
|
||
| // SetVaultTool sets the active vault for this conversation. | ||
| var SetVaultTool = anthropic.Tool{ |
Contributor
There was a problem hiding this comment.
Would it be better to do on creation of a new conversation? Since the conversation is tied to a vault we should be able to get the needed keys on conversation creation? Let me know if im misinterpreting something
7 tasks
d4afa11 to
edb69f7
Compare
Connects the agent backend to a Vultisig MCP server (optional, via MCP_SERVER_URL) so Claude can discover and call external tools like get_eth_balance and get_token_balance. Adds a set_vault built-in tool that binds vault keys (ECDSA, EdDSA, chaincode) to a conversation. On each request the MCP session is automatically primed with the active vault so address derivation happens server-side without the user repeating keys. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add nix flake dev environment with Go, PostgreSQL, Redis, sqlc - Add Transaction type and extract transactions from MCP tool results - Remove redundant public key auth checks (already handled by middleware) - Rename chaincode_hex to chain_code for consistency Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract token search results from the find_token MCP tool and pass them as structured data in the API response so frontend apps can prompt users to add tokens/chains to their vault. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two issues prevented the tokens field from appearing in API responses: 1. CallTool discarded text on IsError — when the MCP tool set IsError: true, CallTool returned a Go error and the text content was lost. Now returns a ToolError that carries the text, so executeTool can still pass it to trackToolResult for structured extraction. 2. trackToolResult assumed pure JSON — MCP tools may return multiple text content blocks (joined with \n) or mix descriptive text with JSON. The direct json.Unmarshal failed silently. Now uses extractTokens() which tries direct unmarshal first, then scans for JSON objects in the text using json.Decoder (which handles trailing content). Also adds diagnostic logging when parsing fails so we can see the actual MCP result text in logs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Integrate MCP resources protocol (resources/list, resources/read) to
discover and load skill guides from the MCP server. Skills are markdown
documents at skills/{slug}.md that provide detailed workflow instructions.
The skill list is injected into the system prompt so the LLM knows what's
available, but skill content is only loaded on-demand via the new get_skill
tool when relevant to the user's request. This keeps the context window
lean as the skill library grows.
- MCP client: add ListSkills, ReadSkill, SkillSummary with TTL caching
- Agent: extend MCPToolProvider interface with skill methods
- Agent: inject skill summary into system prompt after tool descriptions
- Tools: add get_skill native tool (only registered when skills exist)
- Executor: add get_skill handler delegating to MCP ReadSkill
- Main: pre-warm skill cache at startup alongside tools
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The MCP server returns skill resources with URIs like "skill://vultisig/evm-contract-call.md" but extractSkillSlug was filtering for the "skills/" prefix, discarding all entries. - extractSkillSlug now extracts the last path segment before .md, handling any URI scheme (skill://, skills/, etc.) - ReadSkill now looks up the full URI from the skill cache instead of constructing it, so it works with any URI format Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
0ee21c3 to
ece7411
Compare
…rmat OpenRouter's universal format is OpenAI Chat Completions — it translates that to whatever the underlying model needs. Non-Anthropic models were rejecting our Anthropic Messages API format with "Invalid Anthropic Messages API request". - Rewrite client.go types and wire format (ToolCall, AssistantMessage, ToolMessage, ToolChoice with custom MarshalJSON) - URL: /messages → /chat/completions - System prompt: moved from request field to system message - Tool definitions: InputSchema wrapped as function parameters on wire - Response: parsed from choices[0].message with convenience fields - Streaming: OpenAI SSE format (data: chunks, data: [DONE]) - Update agent.go ProcessMessage/ProcessMessageStream for new types - Rename GetAnthropicTools → GetTools in MCP client and interface Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
get_eth_balance,get_token_balance,set_vault_info), and exposes them to Claude alongside existing built-in toolsset_vaultbuilt-in tool that stores vault keys (ecdsa_public_key,eddsa_public_key,chaincode_hex) per-conversation in Postgres, so the MCP server can derive addresses automaticallyWhy this is needed
Users have multiple vaults and switch between them. When Claude calls MCP tools like
get_eth_balance, the MCP server needs to know which vault's keys to use for address derivation. Without per-conversation tracking, the user would need to re-provide keys every time, or worse, the wrong vault's balance could be returned.By binding vault keys to the conversation:
set_vaultonce when selecting a vaultset_vaultagain updates both the DB and the MCP sessionHow it works
MCP client (
internal/mcp/client.go)Initialize()→ListTools()at startup; tools cached in-memory with configurable TTLMcp-Session-IdheaderVault lifecycle
Configuration
MCP_SERVER_URLhttp://localhost:8888)MCP_TOOL_CACHE_TTL_SECONDS300Database
New migration adds three nullable columns to
agent_conversations:ecdsa_public_key TEXTeddsa_public_key TEXTchaincode_hex TEXTTest plan
MCP_SERVER_URL=http://localhost:8888, start server — logs should showmcp tools loadedwith tool countMCP_SERVER_URL, start server — no MCP logs, server works normallyset_vaulttool — verify DB columns populated, MCPset_vault_infocalledmcp session primed with vault info)set_vaultagain with different keys — verify DB updated, new keys forwarded to MCP🤖 Generated with Claude Code